#ifndef BOOST__THREAD__PARAMETERIZED_SCHEDULER_HPP
#  define BOOST__THREAD__PARAMETERIZED_SCHEDULER_HPP

// Copyright (C) 2004-2009
// Alex Turbov, Igor Veresov
//
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)


#  include <boost/thread/details/lock_matrix.hpp>

#  include <boost/call_traits.hpp>
#  include <boost/fusion/include/at_c.hpp>
#  include <boost/fusion/include/front.hpp>
#  include <boost/fusion/include/vector.hpp>
#  include <boost/fusion/mpl/pop_front.hpp>
#  include <boost/fusion/mpl/push_front.hpp>

namespace boost {

/// \note This scheduler assume that template parameter \c Scheduler is default constructable
/// \todo It would be nice to have @c map as templated parameter to allow users change for
/// example allocator type... Hmmmm... from other side it is not good idea to expose internals
/// of this class.
template <typename Scheduler, typename Key>
class parameterized_scheduler
{
public:
    typedef Key key_type;
    typedef Scheduler scheduler_type;
    typedef BOOST_DEDUCED_TYPENAME scheduler_type::matrix_type matrix_type;
    typedef BOOST_DEDUCED_TYPENAME mpl::push_front<
        BOOST_DEDUCED_TYPENAME scheduler_type::lock_params_type
      , key_type
      >::type lock_params_type;
    typedef BOOST_DEDUCED_TYPENAME mpl::push_front<
        BOOST_DEDUCED_TYPENAME scheduler_type::unlock_params_type
      , key_type
      >::type unlock_params_type;
    typedef BOOST_DEDUCED_TYPENAME scheduler_type::request_id_type request_id_type;

private:
    typedef std::map<key_type, scheduler_type> locked_resources_type;
    locked_resources_type m_locked_resources;

public:

    bool try_lock(
        BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p
      , request_id_type request_id
      )
    {
        return m_locked_resources[fusion::front(p)]
          .try_lock(fusion::pop_front(p), request_id);
    }
    void unlock(BOOST_DEDUCED_TYPENAME call_traits<unlock_params_type>::param_type p)
    {
        scheduler_type& s = m_locked_resources[fusion::front(p)];
        s.unlock(fusion::pop_front(p));
        if (!s.is_used())
            m_locked_resources.erase(fusion::front(p));
    }
    //
    bool is_used() const
    {
        return !m_locked_resources.empty();
    }
    request_id_type assign_request_id(BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p)
    {
        return m_locked_resources[fusion::front(p)]
          .assign_request_id(fusion::pop_front(p));
    }
    void unassign_request_id(
        BOOST_DEDUCED_TYPENAME call_traits<lock_params_type>::param_type p
      , request_id_type request_id
      )
    {
        m_locked_resources[fusion::front(p)]
          .unassign_request_id(fusion::pop_front(p), request_id);
    }
};

}
#endif                                                      // #ifndef BOOST__THREAD__PARAMETERIZED_SCHEDULER_HPP
